/*
The surface-physics project: http://code.google.com/p/surface-physics/
Adds real world physics based on the Bullet physics engine, to bases in the Orbiter space
flight simulation software.

Written by Abhijit Nandy
License : Apache License 2.0

Bump_ExternalStaticGeometry.cpp
Bump_ExternalStaticGeometry management implementation.


*/


/* Class declaration */
#include "Bump_ExternalStaticGeometry.h"
#include "../BulletBase/Bump_CollisionShape.h"


Registrar Bump_ExternalStaticGeometry::registrar("ExternalStaticGeometry", Bump_ExternalStaticGeometry::create);


/**
 * Constructor for this derived class
 * Will be used if some attachment specific needs to be initialized comes up
 * Do NOT allocate memory here, let init() do that
 *
 * It is in fact currently used for a temporary vehicle simulation class which
 * should actually be derived from Bump_ExternalStaticGeometry.
 */
Bump_ExternalStaticGeometry::Bump_ExternalStaticGeometry()
{
	mPreStepFrequency = -1;

}


/**
 * Destructor, does nothing currently
 */
Bump_ExternalStaticGeometry::~Bump_ExternalStaticGeometry()
{
	//Destroy physics counterpart
	int rv = destroyBulletRigidBody();
	if(rv){
		sprintf(boutput, "Bump_ExternalStaticGeometry::exit: ERROR : Destroying rigid body failed for %s", strObjectName);
		oapiWriteLog(boutput);
	}

}


/**
 * Initialize a base component.
 * Creates a new BulletBase Attachment and inserts it in the Orbiter world & Bullet World
 *
 *
 */
int
Bump_ExternalStaticGeometry::clbkSetClassCaps(InitialObjectData& initialData)
{
	int rv;

	// This call MUST be made before anything else, sets base
	Bump_BaseObject::clbkSetClassCaps(initialData);

	//oapiWriteLogV("Bump_ExternalStaticGeometry::clbkSetClassCaps: Initializing %s", strObjectName);

	//Create Orbiter object: Load any meshes by reading the mesh files from the physics file
	createOrbiterPrimitives();


	//Create physics counterpart, if this fails, should remove Orbiter meshes that were loaded above
	rv = createBulletRigidBody();
	if(rv){
		//Remove here if necess.

		sprintf(boutput, "Bump_ExternalStaticGeometry::clbkSetClassCaps: ERROR : Creating rigid body failed %s",
				strObjectName);
		oapiWriteLog(boutput);
		return 1;
	}
	
	
	//Only after Orbiter AND Bullet successful, set physics to true
	physicsInited = true;




	return 0;
}


int
Bump_ExternalStaticGeometry::clbkPreStep(double SimT, double SimDT, double MJD)
{
	return 0;
}


/**
 *	Process keyboard input any other messages, not used for this class, why send
 *	commands to terrain, unless we want to distort it in some way ? Damadge simulation ?
 *	Vehicle tracks ?
 */
int
Bump_ExternalStaticGeometry::processCommand()
{
/*	if(bbv->validKeyInput){

		//Command
		switch (bbv->keyInput.key)
		{
			case OAPI_KEY_UP:

				break;

			case OAPI_KEY_SPACE:
				break;



			case NO_KEY:

				break;

			default:
				break;
		}
	}
*/
	return 0;
}


/**
 * Create any Orbiter stuff
 *
 */
int
Bump_ExternalStaticGeometry::createOrbiterPrimitives()
{

	return 0;
}


/**
 * Destroy the created Orbiter stuff
 *
 */
int
Bump_ExternalStaticGeometry::destroyOrbiterPrimitives()
{



	return 0;
}


/**
 * Create the physics counterpart in the Bullet world.
 *
 * Preferably get all info required from Orbiter and store in members beforehand.
 *
 * Optimize: reuse box shape by scaling a unit block for the pad sections and blocks
 *
 * TODO: Parse the base cfg file to get the shapes instead of hard coding ? Or merge
 * 		 base cfg with physics file for base ?
 *
 */
int
Bump_ExternalStaticGeometry::createBulletRigidBody()
{

	btTransform trans;
	btQuaternion q;


	//Create pads from box shapes
	unsigned long int pad;
	double equPadLng, equPadLat, equPadRad;
	VECTOR3 rposPad, gposPad;
	for(pad=0; pad<oapiGetBasePadCount(bumpBase->hObjBase); pad++){
		oapiGetBasePadEquPos(bumpBase->hObjBase, pad, &equPadLng, &equPadLat, &equPadRad);

		oapiEquToGlobal(bumpBase->hObjRefBody, equPadLng, equPadLat, equPadRad, &gposPad);

		bumpBase->bb->Global2Local(gposPad, rposPad);

		/*oapiWriteLogV("Bump_ExternalStaticGeometry::createBulletRigidBody:" \
				"Pad %d: Long:%f, Lat:%f, Rad:%f >> (%f,%f,%f)",
				pad,
				equPadLng, equPadLat, equPadRad,
				rposPad.x, rposPad.y, rposPad.z);*/

		createBulletPad( btScalar(rposPad.x + bumpBase->simBoxOffset.x()),
						 btScalar(rposPad.y + bumpBase->simBoxOffset.y()),
						 btScalar(rposPad.z + bumpBase->simBoxOffset.z()));
	}

	if ( strcmp(bumpBase->strBaseName, "Brighton Beach") == 0) {
		brightonSpecificGeometry();
	}
	else {
		olympusSpecificGeometry();
	}





	return 0;
}


/**
 * Creates a pad at the position passed relative to base
 */
int
Bump_ExternalStaticGeometry::createBulletPad(btScalar rposX,
											 btScalar rposY,
											 btScalar rposZ)
{
	btTransform transPadSection;


	btQuaternion q;

	btScalar r = 40.3f, x = 0.f, z = 0.f, cubeHalfLength = 0.5;
	btScalar turnAngle = 45.0f;
	float i;

	btCollisionShape* boxshpPadSection = new btBoxShape(btVector3(0.2f,
												0.2f,
											16.0f));
	bumpBase->m_collisionShapes->push_back(boxshpPadSection);

	for(i=0; i<360; i+=turnAngle){

		x = r * cos(i* btScalar(PI)/180.f);
		z = r * sin(i* btScalar(PI)/180.f);

		q.setEuler(-i*(btScalar(PI/180.f)),  0, 0);

		transPadSection.setIdentity();
		transPadSection.setRotation(q);
		transPadSection.setOrigin(btVector3(x + rposX, 0.2f + rposY, z + rposZ));

		phyEngine->addRigidBody(0.0f, transPadSection, boxshpPadSection);
	}


	return 0;
}

void
Bump_ExternalStaticGeometry::brightonSpecificGeometry()
{
	//Create Tank, block and train geometry
	btTransform trans;
	btQuaternion q;

	//Here is the tank
	trans.setIdentity();
	trans.setOrigin(btVector3(0.f,+50.f,0.f));
	q.setEuler(0.f, 0.f,  btScalar(PI/2));
	trans.setRotation(q);
	btCollisionShape* cylshpTank =
			new btCylinderShapeX(btVector3(50.f, 25.f, 25.f)); //y ht :half extent, rest are radii

	bumpBase->m_collisionShapes->push_back(cylshpTank);
	phyEngine->addRigidBody(0.0f, trans, cylshpTank);

	//Now the tank top block
	btCollisionShape* boxshpBlock1 = new btBoxShape(btVector3(1.f, 1.f, 1.f));
	bumpBase->m_collisionShapes->push_back(boxshpBlock1);

	trans.setIdentity();
	trans.setOrigin(btVector3(0.f, 100.f + 2.5f, 5.f));
	q.setEuler(0.f, 0.f, 0.f);
	trans.setRotation(q);
	boxshpBlock1->setLocalScaling(btVector3(10.f, 2.5f, 10.f));	
	phyEngine->addRigidBody(0.0f, trans, boxshpBlock1);

	//Now the tank side block
	trans.setIdentity();
	trans.setOrigin(btVector3(20.f, 0.f + 10.f, -40.f));
	q.setEuler(0.f, 0.f, 0.f);
	trans.setRotation(q);
	btCollisionShape* boxshpBlock2 = new btBoxShape(btVector3(1.f, 1.f, 1.f));
	bumpBase->m_collisionShapes->push_back(boxshpBlock2);
	boxshpBlock2->setLocalScaling(btVector3(20.f, 10.f, 40.f));
	phyEngine->addRigidBody(0.0f, trans, boxshpBlock2);



	//No train yet!
}


/**
 *
 */
void
Bump_ExternalStaticGeometry::olympusSpecificGeometry()
{
	btTransform trans;
	btQuaternion q;

	// The tank shape
	btCollisionShape* cylshpTank =
				new btCylinderShapeX(btVector3(6.f, 20.f, 20.f)); //y ht :half extent, rest are radii
	bumpBase->m_collisionShapes->push_back(cylshpTank);

	//Tank 1
	trans.setIdentity();
	trans.setOrigin(btVector3(-40,+6.f,-120.f) + bumpBase->simBoxOffset);
	q.setEuler(0.f, 0.f,  btScalar(PI/2));
	trans.setRotation(q);
	phyEngine->addRigidBody(0.0f, trans, cylshpTank);

	//Tank 2
	trans.setIdentity();
	trans.setOrigin(btVector3(-140,+6.f,-120.f) + bumpBase->simBoxOffset);
	q.setEuler(0.f, 0.f,  btScalar(PI/2));
	trans.setRotation(q);
	phyEngine->addRigidBody(0.0f, trans, cylshpTank);

	//Tank 3
	trans.setIdentity();
	trans.setOrigin(btVector3(-140,+6.f,-20.f) + bumpBase->simBoxOffset);
	q.setEuler(0.f, 0.f,  btScalar(PI/2));
	trans.setRotation(q);
	phyEngine->addRigidBody(0.0f, trans, cylshpTank);

	// Block 1
	btCollisionShape* boxshpBlock1 = new btBoxShape(btVector3(1.f, 1.f, 1.f));
	bumpBase->m_collisionShapes->push_back(boxshpBlock1);
	trans.setIdentity();
	trans.setOrigin(btVector3(30.f, 0.f + 2.5f, -120.f) + bumpBase->simBoxOffset);
	q.setEuler(0.f, 0.f, 0.f);
	trans.setRotation(q);
	boxshpBlock1->setLocalScaling(btVector3(170.f, 2.5f, 2.5f));
	phyEngine->addRigidBody(0.0f, trans, boxshpBlock1);

	// Block 2
	btCollisionShape* boxshpBlock2 = new btBoxShape(btVector3(1.f, 1.f, 1.f));
	bumpBase->m_collisionShapes->push_back(boxshpBlock2);
	trans.setIdentity();
	trans.setOrigin(btVector3(-40.f, 0.f + 2.5f, -220.f) + bumpBase->simBoxOffset);
	q.setEuler(btScalar(PI/2), 0.f, 0.f);
	trans.setRotation(q);
	boxshpBlock2->setLocalScaling(btVector3(100.f, 2.5f, 2.5f));
	phyEngine->addRigidBody(0.0f, trans, boxshpBlock2);

	// Block 3
	btCollisionShape* boxshpBlock3 = new btBoxShape(btVector3(1.f, 1.f, 1.f));
	bumpBase->m_collisionShapes->push_back(boxshpBlock3);
	trans.setIdentity();
	trans.setOrigin(btVector3(-90.f, 0.f + 2.5f, -70.f) + bumpBase->simBoxOffset);
	q.setEuler(btScalar(PI/4), 0.f, 0.f);
	trans.setRotation(q);
	boxshpBlock3->setLocalScaling(btVector3(70.f, 2.5f, 2.5f));
	phyEngine->addRigidBody(0.0f, trans, boxshpBlock3);

	// Hangar shape
	btCollisionShape* cylshpHangar = new btCylinderShapeZ(btVector3(1.f, 1.f, 1.f));
	bumpBase->m_collisionShapes->push_back(cylshpHangar);
	cylshpHangar->setLocalScaling(btVector3(40.f, 40.f, 30.f));

	// Hangar 1 (increasing along x)
	trans.setIdentity();
	trans.setOrigin(btVector3(40.f, -30.f, -120.f) + bumpBase->simBoxOffset);
	phyEngine->addRigidBody(0.0f, trans, cylshpHangar);

	// Hangar 2 (increasing along x)
	trans.setIdentity();
	trans.setOrigin(btVector3(120.f, -30.f, -120.f) + bumpBase->simBoxOffset);
	phyEngine->addRigidBody(0.0f, trans, cylshpHangar);

	// Hangar 3 (increasing along x)
	trans.setIdentity();
	trans.setOrigin(btVector3(200.f, -30.f, -120.f) + bumpBase->simBoxOffset);
	phyEngine->addRigidBody(0.0f, trans, cylshpHangar);

	// Hangar 4 (increasing along x)
	trans.setIdentity();
	trans.setOrigin(btVector3(120.f, -30.f, -250.f) + bumpBase->simBoxOffset);
	phyEngine->addRigidBody(0.0f, trans, cylshpHangar);

	// Hangar 5 (increasing along x)
	trans.setIdentity();
	trans.setOrigin(btVector3(200.f, -30.f, -250.f) + bumpBase->simBoxOffset);
	phyEngine->addRigidBody(0.0f, trans, cylshpHangar);

	// Hangar 6 (decreasing along z)
	trans.setIdentity();
	trans.setOrigin(btVector3(-50.f, -30.f, -220.f) + bumpBase->simBoxOffset);
	q.setEuler(btScalar(PI/2), 0.f, 0.f);
	trans.setRotation(q);
	phyEngine->addRigidBody(0.0f, trans, cylshpHangar);

	// Hangar 7 (decreasing along z)
	trans.setIdentity();
	trans.setOrigin(btVector3(-50.f, -30.f, -320.f) + bumpBase->simBoxOffset);
	q.setEuler(btScalar(PI/2), 0.f, 0.f);
	trans.setRotation(q);
	phyEngine->addRigidBody(0.0f, trans, cylshpHangar);


	//No train yet!
}


/**
 * De-allocates memory being consumed by the raycast vehicle so it can be
 * created again by createBulletRigidBody()
 *
 * This is a very important function. If memory is not freed up, Orbiter sim will not
 * exit properly and the Launchpad will not appear leading to user frustration!
 *
 * Here we de-allocate only the sub class specific stuff which have not been freed by
 * bump.clientResetScene(), which is called before Bump_BulletBase::exit();
 *
 * Thus rigid bodies(derived from CollisionObjects), motion states are already removed
 * by the time we come here, DO NOT remove them again !
 *
 * The stuff which should be removed are any sub class specific classes used to model
 * this object like m_vehicleRayCaster & m_vehicle. Also any collision shapes not pushed
 * into m_collisionShapes[] during init(), needs to be freed here, such as the wheelShape
 */
int
Bump_ExternalStaticGeometry::destroyBulletRigidBody()
{
	physicsInited = false;



	return 0;
}
